Frontend Forever App
We have a mobile app for you to download and use. And you can unlock many features in the app.
Get it now
Intall Later
Run
HTML
CSS
Javascript
Output
Document
∞
ephemeral
*
tic-tac-toe
(2 players)
Dark
☾
Light
☀
Auto
◐
@charset "UTF-8"; @import url(https://fonts.googleapis.com/css?family=Nunito+Sans:300,400,600,700,800); *, :after, :before { box-sizing: border-box; padding: 0; margin: 0; } @import url('https://fonts.googleapis.com/css?family=Poppins:300,400,500,600,700,800,900&display=swap'); @layer reset { *, *:before, *:after { box-sizing: border-box; } img { max-width: 100%; } } html { color-scheme: dark; // color-scheme: light dark; // background: #222; background-color: canvas; background-color: light-dark(#fff, #1e1e1e); background-color: light-dark(#fff, #121312); padding: 1rem; font-family: system-ui; font-family: 'Poppins', sans-serif; font-size: 1rem; body { display: grid; gap: 1.5rem; color: light-dark(#333, #aaa); color: light-dark(#444, #999); max-width: min(40rem, 100%); width: fit-content; // width: min(100%, 42ch); width: min(100%, 42ch); margin: auto; text-align: center; padding-block: .5rem .5rem; } } /// // from: // https://codepen.io/nonsalant/pen/RwzrVEd/783818d57688e2495737dbab076110c1 // @layer v2-2 { body { overflow: auto; position: relative; display: grid; place-content: center; width: 10rem; margin: auto; gap: 1rem; min-height: 90dvh; text-align: center; > * { transition: all .3s var(--back-out); } // background: #88888810; } h1 { --player-1-clr: light-dark( oklch(0.5 0.15 200 / 1), oklch(0.7 0.07 200 / 1) ); --player-2-clr: light-dark( oklch(0.75 0.20 20 / 1), oklch(0.7 0.15 20 / 1) ); --player-clr: var(--player-1-clr); font-family: inherit; text-align: center; margin-block: 0; padding-block: 0; opacity: .6; display: grid; gap: .2rem; // outline: solid 1px yellow; position: relative; // z-index: -1; color: light-dark(#000, #ccc); // color: light-dark(red, #ccc); } body:has(.player-2s-turn) h1 { --player-clr: var(--player-2-clr); } h1 small { font-size: .5em; font-weight: normal; color: #888888; color: light-dark(#555, #777); } .logo { font-size: 18rem; // margin-inline: -2rem; // margin-block-end: -1rem; // margin-block-end: -4.5rem; // padding-block-end: -2rem; margin-block-end: -4.9rem; font-weight: normal; height: 6rem; opacity: .07; display: grid; place-content: center; // outline: solid 1px yellow; // filter: blur(1px); user-select: none; z-index: -1; } kbd { font-size: .9em; font-weight: bold; border: solid 1px #88888880; padding-inline: .5ch; border-radius: 3px; } hr { width: 100%; opacity: .25; } h1 sup { color: light-dark(#333, #ccc); font-size: 1.2rem; font-weight: normal; display: inline-block; display: block; font-family: "Georgia"; position: absolute; right: -1ch; top: 0; user-select: none; } .description { // text-wrap: balance; text-align: left; line-height: 1.6; line-height: 1.75; margin: 0 .25rem; padding: 0; padding-inline: .5rem; // outline: solid 1px red; position: relative; display: block; sup { font-family: "Georgia"; font-size: 1.5rem; position: absolute; top: 1.175ch; left: -.5ch; } em { // color: var(--player-clr); font-style: normal; // font-weight: bold; } } .adjective { color: var(--player-clr); font-family: sans-serif; font-family: 'Poppins', sans-serif; text-transform: uppercase; font-weight: normal; font-style: normal; font-size: 0.75em; font-size: 0.35em; line-height: 1; letter-spacing: 9.25px; letter-spacing: 13.76px; position: relative; transition: color 0.3s var(--back-out); width: fit-content; margin-inline: auto; // margin-block-end: .2rem; padding-left: .75ch; } b, i { font-family: inherit; font-weight: bold; font-style: normal; transition: all .3s var(--back-out); display: inline-grid; place-content: center; line-height: .75em; width: .75rem; height: .75rem; position: relative; z-index: 1; } b { color: var(--player-1-clr); transform: scale(2) translatey(-.2rem); // transform-origin: center center; } i { color: var(--player-2-clr); } .fade-out { opacity: 75%; filter: grayscale(75%); // font-weight: normal; } // buttons button { cursor: pointer; color: inherit; color: light-dark(#000, #fff); color: ButtonText; background-color: light-dark(ButtonFace, #444); // text-transform: uppercase; border: transparent; padding: .25rem .5rem; border-radius: 4px; translate: 0 3px; width: max-content; margin-inline: auto; min-width: 17ch; // letter-spacing: 1px; transition: opacity .6s var(--back-out), background-color .3s var(--back-out), translate .6s var(--back-out); &:hover { background-color: #88888840; } &:focus-visible { outline: dashed 1px currentcolor; outline-offset: -3px; } &:active { filter: scale(.9); } } table.xo:not(:has(b)) + footer button { visibility: hidden; opacity: 0; translate: 0 -1rem; pointer-events: none; } table.xo:has(b) + footer button { visibility: unset; opacity: 1; translate: 0 0; pointer-events: unset; } /// body { // --player-1-clr: light-dark(#aaaaff, #8888ff); // --player-2-clr: light-dark(#ffaaaa, #ff8888); // --player-clr: var(--player-1-clr); --player-1-clr: light-dark( oklch(0.5 0.15 200 / 1), oklch(0.7 0.07 200 / 1) ); --player-2-clr: light-dark( oklch(0.75 0.20 20 / 1), oklch(0.7 0.15 20 / 1) ); --player-clr: var(--player-1-clr); &:has(.player-2s-turn) { --player-clr: var(--player-2-clr); } --shadow-color: 220 40% 2%; --shadow-strength: 25%; --inner-shadow-highlight: inset 0 -.5px 0 0 #fff1, inset 0 .5px 0 0 #0007; --shadow-6: 0 -1px 2px 0 hsl(var(--shadow-color) / calc(var(--shadow-strength) + 2%)), 0 3px 2px -2px hsl(var(--shadow-color) / calc(var(--shadow-strength) + 3%)), 0 7px 5px -2px hsl(var(--shadow-color) / calc(var(--shadow-strength) + 3%)), 0 12px 10px -2px hsl(var(--shadow-color) / calc(var(--shadow-strength) + 4%)), 0 22px 18px -2px hsl(var(--shadow-color) / calc(var(--shadow-strength) + 5%)), 0 41px 33px -2px hsl(var(--shadow-color) / calc(var(--shadow-strength) + 6%)), 0 100px 80px -2px hsl(var(--shadow-color) / calc(var(--shadow-strength) + 7%)); --back-out: linear( 0 0%, 0.1935 4.37%, 0.3671 8.83%, 0.521 13.38%, 0.6557 18.04%, 0.7716 22.82%, 0.869 27.73%, 0.9488 32.81%, 1.0111 38.08%, 1.0512 42.81%, 1.0792 47.75%, 1.0953 52.97%, 1.1 58.55%, 1.0956 63.36%, 1.0838 68.73%, 1.0119 90.98%, 1.0029 95.69%, 1 100% ); } table.xo { --player-1-clr: light-dark( oklch(0.75 0.075 200 / 1), oklch(0.7 0.07 200 / 1) ); --_block-size: 7rem; --_gap: .3rem; --_cursor-1: url('data:image/svg+xml;utf8,
⨯
'); --_cursor-2: url('data:image/svg+xml;utf8,
⭘
'); // filter: invert(1); // background: #000; width: 100%; user-select: none; border-spacing: var(--_gap); grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(3, 1fr); position: relative; // max-width: min(20rem, 100%, 90vmin); // aspect-ratio: 1; margin-inline: auto; // isolation: isolate; overflow: hidden; /// &:active { opacity: .98; } &:not(.player-2s-turn) { cursor: var(--_cursor-1) 4 16, auto; } &.player-2s-turn { cursor: var(--_cursor-2) 4 16, auto; } &:active { td { transition: opacity .6s linear; } // td:not(:hover) { opacity: .98; } // td { transition: opacity .3s linear; } // td:not(:hover) { opacity: .75; } cursor: var(--_cursor-2) 4 14, auto; &.player-2s-turn { cursor: var(--_cursor-1) 4 14, auto; } } &:not(:active):has(td:focus-visible) { cursor: default; } // /* board shadows */ // &::before { // content: ""; // // background: red; // // box-shadow: inset 0 0 10px red; // background: radial-gradient( // ellipse at center, // rgba(0,0,0,0) 0%, // rgba(0,0,0,.5) 60%, // rgba(0,0,0,.75) 80%, // rgba(0,0,0,.9) 95%, // rgba(0,0,0,1) 100% // ); // position: absolute; // // inset: 1px; // inset: 0; // // inset: var(--_gap) 0 0 0; // inset: var(--_gap) 0 var(--_gap) 0; // pointer-events: none; // // z-index: -1; // // z-index: 99; // z-index: 1; // opacity: 1; // } // &:hover::before { // opacity: 0; // } /* board bg */ &::before { content: ""; // background: red; // box-shadow: inset 0 0 10px red; background: #000; background: light-dark(#222, transparent); position: absolute; // inset: 1px; inset: 0; // inset: var(--_gap) 0 var(--_gap) 0; inset: var(--_gap) 0 0 0; pointer-events: none; // z-index: -1; // z-index: 99; z-index: -1; opacity: 1; } /* surfaces */ // &:hover td { // border: solid 1px #88888860; // border: solid 1px #888888; // // border-color: color-mix(in hsl, var(--player-clr) 50%, transparent); // } td { // aspect-ratio: 1; position: relative; background: #000; background: light-dark(#333, #000); padding: 1rem; border: solid 1px #88888899; // border-color: color-mix(in hsl, var(--player-clr) 80%, transparent); border-radius: 2px; // min-height: 3rem; // min-width: 3rem; height: var(--_block-size); width: var(--_block-size); // display: inline-grid; place-content: center; transition: border .6s var(--back-out); @starting-style { border-color: transparent; } } td:hover { // border-color: #00000040; // border-color: color-mix(in hsl, var(--player-clr) 35%, transparent); background-attachment: fixed; background-image: radial-gradient( circle at var(--x, 0) var(--y, 0), hsl(0 0% 100% / 0.175), hsl(0 0% 100% / 0.0250) 7.5rem // // color-mix(in hsl, var(--player-clr) 17.5%, transparent), // // color-mix(in hsl, var(--player-clr) 2.5%, transparent) 7.5rem // color-mix(in hsl, var(--player-clr) 35%, transparent), // color-mix(in hsl, var(--player-clr) 5%, transparent) 7.5rem ); } /* edges */ td::after { content: ""; position: absolute; inset: -2px; border-radius: inherit; background-attachment: fixed; background-color: #000; background-image: radial-gradient( circle at var(--x, 0) var(--y, 0), hsl(0 0% 100% / 0.95), // color-mix(in hsl, var(--player-clr) 95%, transparent), transparent 2rem ); pointer-events: none; z-index: -1; } /* winner strokes */ &::after { background: linear-gradient(to var(--_to), transparent calc(var(--_distance) - 1px), var(--_color) calc(var(--_distance) - 1px), var(--_color) calc(var(--_distance) + 1px), transparent calc(var(--_distance) + 1px) ); display: block; pointer-events: none; display: none; content: ""; position: absolute; inset: .5rem; inset: calc(var(--_gap) * 4); border-radius: 1rem; z-index: 1; opacity: 1; // transition: all 1.3s ease-in-out; // transition-delay: 1.3s; } /// from: https://codepen.io/nonsalant/pen/RwzrVEd/783818d57688e2495737dbab076110c1 td { // text-align: center; // vertical-align: middle; // padding-block: 1rem; &:not(:has(:is(b,i))) { // cursor: default; } outline-offset: -1.25rem; // border-radius: .25rem; } td:focus-visible { outline: dashed 1px var(--player-1-clr); } &.player-2s-turn td:focus-visible { outline: dashed 1px var(--player-2-clr); } /// td footer { position: absolute; bottom: var(--_gap); left: 50%; translate: -50% 0; display: flex; } } button .new-game { display: none; } footer .win { display: none; line-height: 1; } *:has(> table.xo) .turn { // --duration: 4s; --repeat: 1; & > span { display: block; font-style: italic; } & > span > span { color: #888; } .player-1 { color: var(--player-1-clr); display: block; line-height: 1; } .player-2 { color: var(--player-2-clr); display: none; line-height: 1; } } *:has(> table.xo.player-2s-turn) .turn { display: grid; place-content: center; .player-1 { display: none; } .player-2 { display: block; } } footer { // --player-1-clr: light-dark( oklch(0.5 0.15 200 / 1), oklch(0.7 0.07 200 / 1) ); // --player-2-clr: light-dark( oklch(0.75 0.20 20 / 1), oklch(0.7 0.15 20 / 1) ); --player-clr: var(--player-2-clr); display: grid; gap: 1rem; } .asterisk { color: var(--player-2-clr); } body:has(.player-2s-turn) :is(h1, footer) .asterisk { color: var(--player-1-clr); } /// WIN CONDITIONS @mixin win-condition($player-symbol, $pair1, $pair2, $pair3) { table.xo:has(tr:nth-child(#{nth($pair1, 1)}) td:nth-child(#{nth($pair1, 2)}) #{$player-symbol}:not(.fade-out) ):has(tr:nth-child(#{nth($pair2, 1)}) td:nth-child(#{nth($pair2, 2)}) #{$player-symbol}:not(.fade-out) ):has(tr:nth-child(#{nth($pair3, 1)}) td:nth-child(#{nth($pair3, 2)}) #{$player-symbol}:not(.fade-out) ) { // outline: dashed 1px var(--player-#{if($player-symbol == 'b', 1, 2)}-clr); pointer-events: none; & + footer button { .new-game { display: unset; } .reset { display: none; } } & + footer button:has(.new-game) { filter: invert(1); // transition: opacity .3s, display; // transition-behavior: allow-discrete; // font-weight: bold; letter-spacing: 1px; // @starting-style { // opacity: 0; // } &:hover { background-color: #aaa; } } ~ footer .player-1 { display: block; color: var(--player-2-clr); } &.player-2s-turn ~ footer { span.player-1 { display: none; } span.player-2 { display: block; color: var(--player-1-clr); } } ~ footer .turn { display: none; } ~ .pyro { display: block; } ~ .ttt-winner { display: grid; } // &.player-2s-turn ~ .ttt-winner { --_hue-rotation: hue-rotate(200deg); } // winner strokes &::after { display: block !important; // --_color: var(--player-#{if($player-symbol == 'b', 2, 1)}-clr); // light-dark( oklch(0.5 0.15 200 / 1), oklch(0.7 0.07 200 / 1) ) // light-dark( oklch(0.75 0.20 20 / 1), oklch(0.7 0.15 20 / 1) ) --_color: #{if($player-symbol == 'b', 'light-dark( oklch(0.5 0.15 200 / 1), oklch(0.7 0.07 200 / .75) )', 'light-dark( oklch(0.75 0.20 20 / 1), oklch(0.7 0.15 20 / .75) )' )}; } // table background margin-block-end: -.125rem; &::before { inset: var(--_gap) 0; } /* horizontal 1 */ &:has(tr:nth-child(1) td:nth-child(1) #{$player-symbol} ):has(tr:nth-child(1) td:nth-child(2) #{$player-symbol} ):has(tr:nth-child(1) td:nth-child(3) #{$player-symbol} )::after { --_distance: calc(16.66% - (2 * var(--_gap)) + 0px); --_to: bottom; } /* horizontal 2 */ &:has(tr:nth-child(2) td:nth-child(1) #{$player-symbol} ):has(tr:nth-child(2) td:nth-child(2) #{$player-symbol} ):has(tr:nth-child(2) td:nth-child(3) #{$player-symbol} )::after { --_distance: calc(50% - 1px); --_to: bottom; } /* horizontal 3 */ &:has(tr:nth-child(3) td:nth-child(1) #{$player-symbol} ):has(tr:nth-child(3) td:nth-child(2) #{$player-symbol} ):has(tr:nth-child(3) td:nth-child(3) #{$player-symbol} )::after { --_distance: calc(83.33% + (2 * var(--_gap)) - 2px); --_to: bottom; } /* vertical 1 */ &:has(tr:nth-child(1) td:nth-child(1) #{$player-symbol} ):has(tr:nth-child(2) td:nth-child(1) #{$player-symbol} ):has(tr:nth-child(3) td:nth-child(1) #{$player-symbol} )::after { --_distance: calc(16.66% - (2 * var(--_gap)) - 1.5px); --_to: right; } /* vertical 2 */ &:has(tr:nth-child(1) td:nth-child(2) #{$player-symbol} ):has(tr:nth-child(2) td:nth-child(2) #{$player-symbol} ):has(tr:nth-child(3) td:nth-child(2) #{$player-symbol} )::after { --_distance: 50%; --_to: right; } /* vertical 3 */ &:has(tr:nth-child(1) td:nth-child(3) #{$player-symbol} ):has(tr:nth-child(2) td:nth-child(3) #{$player-symbol} ):has(tr:nth-child(3) td:nth-child(3) #{$player-symbol} )::after { --_distance: calc(83.33% + (2 * var(--_gap)) + 1.5px); --_to: right; } /* diagonal 1 */ &:has(tr:nth-child(1) td:nth-child(1) #{$player-symbol} ):has(tr:nth-child(2) td:nth-child(2) #{$player-symbol} ):has(tr:nth-child(3) td:nth-child(3) #{$player-symbol} )::after { --_distance: 50%; --_to: top right; } /* diagonal 2 */ &:has(tr:nth-child(1) td:nth-child(3) #{$player-symbol} ):has(tr:nth-child(2) td:nth-child(2) #{$player-symbol} ):has(tr:nth-child(3) td:nth-child(1) #{$player-symbol} )::after { --_distance: 50%; --_to: top left; } } // end of table.xo:has():has():has() } // end of mixin // Player 1 (X) wins @include win-condition('b', (1, 1), (1, 2), (1, 3)); @include win-condition('b', (2, 1), (2, 2), (2, 3)); @include win-condition('b', (3, 1), (3, 2), (3, 3)); @include win-condition('b', (1, 1), (2, 1), (3, 1)); @include win-condition('b', (1, 2), (2, 2), (3, 2)); @include win-condition('b', (1, 3), (2, 3), (3, 3)); @include win-condition('b', (1, 1), (2, 2), (3, 3)); @include win-condition('b', (1, 3), (2, 2), (3, 1)); // Player 2 (O) wins @include win-condition('i', (1, 1), (1, 2), (1, 3)); @include win-condition('i', (2, 1), (2, 2), (2, 3)); @include win-condition('i', (3, 1), (3, 2), (3, 3)); @include win-condition('i', (1, 1), (2, 1), (3, 1)); @include win-condition('i', (1, 2), (2, 2), (3, 2)); @include win-condition('i', (1, 3), (2, 3), (3, 3)); @include win-condition('i', (1, 1), (2, 2), (3, 3)); @include win-condition('i', (1, 3), (2, 2), (3, 1)); /// end of WIN CONDITIONS .xo .remaining-life { position: absolute; top: .5ch; left: .75ch; z-index: 1; } .remaining-life { white-space: nowrap; display: flex; gap: .5ch; align-items: center; justify-content: center; background: #88888830; background: light-dark(#88888840, #88888825); // border: solid 1px #66666620; border: transparent; border-radius: 5px; padding-inline: 0.7ch; padding-block: .25ch; line-height: 0; } .circle { width: .35rem; height: .35rem; border-radius: 50%; margin: 1px; /* Space between circles */ display: inline-block; /* Aligns circles next to each other */ } .outlined { background-color: light-dark(#888888a0, #444444a0); /* No fill */ border: 1px solid #4caf50; /* Outline color */ border-color: #88888880; } .filled { background-color: #4caf50a0; background-color: light-dark(#4caf50aa, #4caf5080); /* Fill color */ // border: 2px solid transparent; /* No outline */ // border: 1px solid #4caf5040; } // https://codepen.io/nonsalant/pen/xxQxmPq/24411d4e1bf78f5d55368bb2b647ff43 .rubberband { --duration: 3s; --repeat: 3; animation: rubberband var(--duration) ease-in-out var(--repeat); /* animation-delay: 3s; */ } @keyframes rubberband { 0% { transform: scaleX(1) } 19.8% { transform: scale3d(1.125, .875, 1) } 26.4% { transform: scale3d(.875, 1.125, 1) } 33% { transform: scale3d(1.075, .925, 1) } 42.9% { transform: scale3d(.975, 1.025, 1) } 49.5% { transform: scale3d(1.025, .975, 1) } 66% { transform: scaleX(1) } } /// // https://codepen.io/yshlin/pen/WNMmQX.css // pyro @layer pyro { $particles: 50; $width: 500; $height: 500; // Create the explosion... $box-shadow: (); $box-shadow2: (); @for $i from 0 through $particles { $box-shadow: $box-shadow, random($width)-$width / 2 + px random($height)-$height / 1.2 + px hsl(random(360), 100, 50); $box-shadow2: $box-shadow2, 0 0 #fff } @mixin keyframes ($animationName) { @-webkit-keyframes #{$animationName} { @content; } @-moz-keyframes #{$animationName} { @content; } @-o-keyframes #{$animationName} { @content; } @-ms-keyframes #{$animationName} { @content; } @keyframes #{$animationName} { @content; } } @mixin animation-delay ($settings) { -moz-animation-delay: $settings; -webkit-animation-delay: $settings; -o-animation-delay: $settings; -ms-animation-delay: $settings; animation-delay: $settings; } @mixin animation-duration ($settings) { -moz-animation-duration: $settings; -webkit-animation-duration: $settings; -o-animation-duration: $settings; -ms-animation-duration: $settings; animation-duration: $settings; } @mixin animation ($settings) { -moz-animation: $settings; -webkit-animation: $settings; -o-animation: $settings; -ms-animation: $settings; animation: $settings; } @mixin transform ($settings) { transform: $settings; -moz-transform: $settings; -webkit-transform: $settings; -o-transform: $settings; -ms-transform: $settings; } body { margin:0; padding:0; // background: #000; // overflow: hidden; } .pyro > .before, .pyro > .after { position: absolute; width: 5px; height: 5px; border-radius: 50%; box-shadow: $box-shadow2; @include animation((1s bang ease-out infinite backwards, 1s gravity ease-in infinite backwards, 5s position linear infinite backwards)); } .pyro > .after { @include animation-delay((1.25s, 1.25s, 1.25s)); @include animation-duration((1.25s, 1.25s, 6.25s)); } @include keyframes(bang) { to { box-shadow:$box-shadow; } } @include keyframes(gravity) { to { @include transform(translateY(200px)); opacity: 0; } } @include keyframes(position) { 0%, 19.9% { margin-top: 10%; margin-left: 40%; } 20%, 39.9% { margin-top: 40%; margin-left: 30%; } 40%, 59.9% { margin-top: 20%; margin-left: 70% } 60%, 79.9% { margin-top: 30%; margin-left: 20%; } 80%, 99.9% { margin-top: 30%; margin-left: 80%; } } } // overrides .pyro { z-index: 3; inset: 0; width: 100vw; margin: auto; position: fixed; display: none; pointer-events: none; } // winner gif .ttt-winner { --_hue-rotation: hue-rotate(345deg); position: absolute; inset: 0; top: 15rem; // left: auto; // right: auto; margin: auto; margin-top: 0; justify-self: center; aspect-ratio: 1.2; display: grid; /**/ display: none; /**/ overflow: hidden; box-shadow: var(--shadow-6); z-index: 1; cursor: pointer; max-width: min(12rem, 100%); height: auto; object-fit: cover; /** filter: hue-rotate(200deg); filter: hue-rotate(345deg); filter: var(--_hue-rotation); /**/ transition: opacity .3s var(--back-out), filter .3s var(--back-out); // transition-delay: .3s; @starting-style { opacity: 0; } &:hover, &:focus-visible { /**/ filter: grayscale(1); /**/ opacity: .4; opacity: .75; } &:focus-visible { outline: dashed 2px currentcolor; outline-offset: -4px; } &.not-shown { opacity: 0; pointer-events: none; } } /* Theme Picker */ /**/ *:has(.theme-picker) { position: static; } /**/ /**/ html:has(.theme-picker) { position: relative; } /**/ .theme-picker { --color-picker-bg: var(--primary-vivid, #88888860); --color-picker-color: var(--surface-base); --color-picker-hover-color: var(--surface-min); --color-picker-shadow: var(--shadow-1); list-style: none; padding: 0; margin: 0; text-align: right; position: fixed; position: absolute; top: auto; top: 45px; right: 45px; z-index: 3; /* border: solid 1px; */ display: grid; justify-items: end; align-items: center; gap: .5rem; min-width: 85px; /* Hide initially, show when JS loads */ &:not(:focus-within) { opacity: 0; pointer-events: none; } /* &:hover:focus-within { gap: 0; .picker-label { display: none; } } */ /* Only show the next option */ & > *:not(:nth-child(2)) { display: none; } & button { /* https://www.trysmudford.com/blog/a-good-reset/ */ -webkit-appearance: none; box-shadow: none; cursor: pointer; /**/ border: 0; /**/ margin: 0; border-radius: 2em; min-height: 2.2rem; min-width: 2.2rem; display: grid; place-content: center; grid-template-columns: 1fr auto; align-items: center; justify-content: center; justify-items: center; gap: .5ch; padding: .5em 1ch; line-height: 1; background-color: var(--color-picker-bg); color: var(--color-picker-color); font-size: inherit; font-family: system-ui, serif; box-shadow: var(--color-picker-shadow); transition: padding .8s cubic-bezier(.5, .75, .75, 1.25), gap .4s cubic-bezier(.5, .75, .75, 1.25), margin .4s cubic-bezier(.5, .75, .75, 1.25), border-radius .2s cubic-bezier(.5, .75, .75, 1.25); &:hover { color: var(--color-picker-hover-color); /**border-radius: 6px;/**/ margin-right: -3px; } &:active { /** filter: invert(1) hue-rotate(180deg); /**/ color: var(--color-picker-color); /**border-radius: 8px;/**/ cursor: wait; } } & button .icon { text-align: center; width: 1em; height: 1em; display: grid; place-content: center; padding-inline: 0; padding: .1em; } & button .picker-label { transition: all .3s ease-in-out; padding-inline: .25em .15em; } &:not(:is(:hover,:focus-within)) button { gap: 0; } &:not(:is(:hover,:focus-within)) button > *:not(.icon) { display: none; } @media (width < 890px) { /* outline: solid 1px red; */ top: .5rem; right: .5rem; /**/scale: .75;/**/ } transition: top .3s ease-in-out, right .3s ease-in-out; } // https://codepen.io/nonsalant/pen/eYwpZRR/edfd9e9eca31e2a2a9fd83d1f909fe0c .hover-slide { background: linear-gradient(var(--player-clr) 0 0) var(--p, 0) / var(--p, 0) no-repeat; padding-inline: .07em; text-decoration: none; display: inline-block; width: max-content; transition: 0.4s, background-position 0s; &:hover { --p: 100%; // color: #fff !important; color: light-dark(#000,#fff) !important; } } .built-by { font-family: "Georgia"; text-decoration: none; color: currentcolor; white-space: nowrap; } .info-game { font-size: .75em; text-align:center; color: #888; display: flex; gap: .25rem; flex-wrap: wrap; margin-block: 0.25rem; // .remaining-life { background: #222; border-radius: 5px; padding-inline: .75ch; } } .marked { opacity: 0; transition: opacity .6s var(--back-out); } .game-status { // outline: solid 1px red; max-height: 1rem; } .player-2, .player-1 { font-family: system-ui; }
console.log("Event Fired") // shiny edges const grid = document.querySelector(".xo"); document.addEventListener("mousemove", (e) => { grid.style.setProperty("--x", e.x + "px"); grid.style.setProperty("--y", e.y + "px"); }); /// // tic-tac-toe class LimitedArray { constructor(maxLength) { this.maxLength = maxLength; this.items = []; } add(content, row, column) { // remove item if (this.items.length >= this.maxLength) { const removedItem = this.items.shift(); // console.log(removedItem.row, removedItem.column, "emptied"); this.disappear(removedItem.row, removedItem.column); } // .remaining-life // fade out items (mark for removal) let item; if (this.items.length === 1) { item = this.items.at(0); if (item) this.fadeOut3(item.row, item.column); } if (this.items.length === 2) { item = this.items.at(1); if (item) this.fadeOut3(item.row, item.column); } if (this.items.length === 3) { item = this.items.at(0); if (item) this.fadeOut2(item.row, item.column); item = this.items.at(2); if (item) this.fadeOut3(item.row, item.column); } if (this.items.length === 4) { item = this.items.at(1); if (item) this.fadeOut2(item.row, item.column); item = this.items.at(3); if (item) this.fadeOut3(item.row, item.column); } if (this.items.length === 5) { item = this.items.at(0); if (item) this.fadeOut(item.row, item.column); item = this.items.at(2); if (item) this.fadeOut2(item.row, item.column); item = this.items.at(4); if (item) this.fadeOut3(item.row, item.column); } if (this.items.length === 6) { item = this.items.at(1); if (item) this.fadeOut(item.row, item.column); item = this.items.at(3); if (item) this.fadeOut2(item.row, item.column); item = this.items.at(5); if (item) this.fadeOut3(item.row, item.column); } // add item this.items.push({ content, row, column }); // console.log(this.items); } getItems() { return this.items; } size() { return this.items.length; } reset() { this.items=[]; } // disappear(row, column) { // const el = document.querySelector(`table tr:nth-child(${row}) td:nth-child(${column})`); // el.innerHTML = " "; // } disappear(row, column) { const el = document.querySelector(`table tr:nth-child(${row}) td:nth-child(${column})`); const content = el.querySelector(":is(b,i)"); content.classList.add("marked"); setTimeout(() => { // Set a timeout to allow the CSS transition to take effect // Wait for the opacity animation to finish content.addEventListener("transitionend", () => { // Set the innerHTML to a space after the animation is complete el.innerHTML = " "; }, { once: true }); // listener is removed after it fires 1 }, 0); // Use a timeout to allow the class addition to take effect } fadeOut(row, column) { const el = document.querySelector(`table tr:nth-child(${row}) td:nth-child(${column}) > :is(b,i)`); el.classList.add("fade-out"); // el.classList.remove("fade-out-2"); el.closest("td").querySelector(".remaining-life").innerHTML = `
`; } fadeOut2(row, column) { const el = document.querySelector(`table tr:nth-child(${row}) td:nth-child(${column}) > :is(b,i)`); // el.classList.add("fade-out-2"); el.closest("td").querySelector(".remaining-life").innerHTML = `
`; } fadeOut3(row, column) { const el = document.querySelector(`table tr:nth-child(${row}) td:nth-child(${column}) > :is(b,i)`); // el.classList.add("fade-out-2"); el.closest("td").querySelector(".remaining-life").innerHTML = `
`; } } const moves = new LimitedArray(6); const table = document.querySelector("table.xo"); document.querySelectorAll(".xo td").forEach((item, index) => { item.addEventListener("click", function(e) { const el = e.target.closest("td"); if (el.querySelector(":is(b,i)")) return; let player2sTurn = table.classList.contains("player-2s-turn"); let content = player2sTurn ? "
⭘
" : "
⨯
"; content += `
`; table.classList.toggle("player-2s-turn"); const row = Math.floor(index / 3) + 1; const column = index % 3 + 1; moves.add(content, row, column); // console.log(row, column, "filled"); el.innerHTML = content; }); // keyboard item.addEventListener("keydown", function(e) { const el = e.target.closest("td"); // Handle click actions for space and enter keys if (e.key === " " || e.key === "Enter") { // if (el.querySelector(":is(b,i)")) { // return; // } el.click(); e.preventDefault(); return; } // Handle Keyboard Navigation const actieEl = document.activeElement; const row = actieEl.parentElement.rowIndex + 1; const col = actieEl.cellIndex + 1; // Function to get the next cell based on row and column adjustments const getNextCell = (rowAdjustment, colAdjustment) => { const newRow = (row + rowAdjustment - 1 + 3) % 3 + 1; // Wrap around rows const newCol = (col + colAdjustment - 1 + 3) % 3 + 1; // Wrap around columns return document.querySelector(`.xo tr:nth-child(${newRow}) td:nth-child(${newCol})`); }; // Directional mapping for navigation keys const directionMap = { ArrowDown: [1, 0], ArrowUp: [-1, 0], ArrowLeft: [0, -1], ArrowRight: [0, 1], w: [-1, 0], s: [1, 0], a: [0, -1], d: [0, 1] }; const adjustment = directionMap[e.key]; if (adjustment) { getNextCell(adjustment[0], adjustment[1]).focus(); e.preventDefault(); } }); // mousedown // item.addEventListener("mousedown", function(e) { // const el = e.target.closest("td"); // el.click(); // }); }); // buttons document.querySelector(".reset-btn").addEventListener("click", e=>{ document.querySelectorAll(".xo td").forEach(item=> { item.innerHTML = " "; table.classList.remove("player-2s-turn"); moves.reset(); document.querySelector(".not-shown")?.classList.remove("not-shown") }) }); // display winner gif document.querySelector(".ttt-winner")?.addEventListener("click", function(e) { const el = e.target.closest("img"); el.classList.add("not-shown"); document.querySelector(".pyro").style.opacity = 0; }); document.querySelector(".ttt-winner")?.addEventListener("keydown", function(e) { const el = e.target.closest("img"); if (e.key === " " || e.key === "Enter") { el.click(); e.preventDefault(); return; } }); // init theme picker const initialTheme = localStorage.getItem('theme') ?? "dark"; if (initialTheme) { const el = document.documentElement; // const currentScheme = el.style.colorScheme || "light"; if (initialTheme === "light dark") { el.style.colorScheme = "light dark"; } else if (initialTheme === "light") { el.style.colorScheme = "light"; } else { el.style.colorScheme = "dark"; } } // theme picker events const themePicker = document.querySelector('.theme-picker'); themePicker.style.opacity = "unset"; themePicker.style.pointerEvents = "unset"; themePicker.addEventListener('click', function (event) { const clickedButton = event.target.closest('button'); if (clickedButton) { const buttons = Array.from(themePicker.querySelectorAll('button')); const clickedIndex = buttons.indexOf(clickedButton); const reorderedButtons = buttons.slice(clickedIndex).concat(buttons.slice(0, clickedIndex)); themePicker.innerHTML = ''; // Clear the existing list reorderedButtons.forEach(button => { themePicker.appendChild(document.createElement('li')).appendChild(button); }); // Focus on the next option let nextOption = themePicker.querySelector("& > *:nth-child(2) button"); nextOption.focus(); // Remove that focus if a mouse was // (presumably) used and now it's leaving nextOption.addEventListener('mouseleave', function() { this.blur(); }); let theme = clickedButton.classList.contains('picker-light') ? "dark" : "light"; if (clickedButton.classList.contains('picker-auto')) theme = "auto"; const el = document.documentElement; const currentScheme = el.style.colorScheme || "light"; // console.log(currentScheme) if (theme === 'auto') { el.style.colorScheme = "light dark"; // localStorage.removeItem('theme'); localStorage.setItem('theme', "light dark"); } else { if (theme === "light") { el.style.colorScheme = "dark"; localStorage.setItem('theme', "dark"); } else { el.style.colorScheme = "light"; localStorage.setItem('theme', "light"); } } } });